/**
 * WTF Engine
 *
 * License... etc.
 **
 * Scripting Support. This file exports the wtf namespaces
 * and creates important bindings for objects under each namespace.
 *
 * Features:
 * 	- Python bindings to various aspects of the engine
 * 
 **
 * Authors: Sean Micklethwaite
 **/


#include "physics.h"
#include "scripting.h"
#include "input.h"
#include "string.h"
#include "gui_console.h"
#include "tools_object_transform.h"

#include "data.h"
#include "pmodel.h"
#include "3dx.h"
#include "3d_primitives.h"
#include "observable.h"

using namespace WtfEngine;
using namespace WtfGraphics;
using namespace boost::python;

class GameStateWrapper: public GameState, public boost::python::wrapper<GameState> {
public:
	GameStateWrapper() : GameState() {};

protected:
	/// Called when the state becomes active
	void Enable() {
		try{
			object o = this->get_override("Enable");
			if(o.ptr() != Py_None) {
				o();
			} else {
				PyErr_Clear();
			};
		}catch(const boost::python::error_already_set & e){
			PyErr_Print();
		};
	};
	/// Called when the state becomes inactive
	void Disable() {
		try{
			object o = this->get_override("Disable");
			if(o.ptr() != Py_None) {
				o();
			} else {
				PyErr_Clear();
			}
		}catch(const boost::python::error_already_set & e){
			PyErr_Print();
		};
	};
	/// Called when the state enters the stack
	void Initialize() {
		try{
			object o = this->get_override("Initialize");
			if(o.ptr() != Py_None) {
				o();
			} else {
				PyErr_Clear();
			};
		}catch(const boost::python::error_already_set & e){
			PyErr_Print();
		};
	};
	/// Called when the state leaves the stack
	void Destroy() {
		try{
			object o = this->get_override("Destroy");
			if(o.ptr() != Py_None) {
				object();
			} else {
				PyErr_Clear();
			};
		}catch(const boost::python::error_already_set & e){
			PyErr_Print();
		};
	};

	GC_AUTOSIZE(GameStateWrapper);
};

/**
 * Handles conversion of string pool entries into python strings.
 **/
struct StringPoolEntryConverter {
	static PyObject* convert(StringPool::Entry const& x) {
		return PyString_FromString((*x).c_str());
    }
    static PyTypeObject const* get_pytype() {
		return &PyString_Type;
    }
};

/**
 * Exports the WtfEngine namespace.
 **/
BOOST_PYTHON_MODULE(WtfEngine)
{
	/**** The Kernel ****/
	
	class_<MemoryManager, boost::noncopyable>("MemoryManager")
		.def("GetInst", &MemoryManager::GetInstancePtr,
				 return_value_policy<reference_existing_object>())
		.staticmethod("GetInst")
		.def("Reclaim", &MemoryManager::Reclaim)
		.def("DumpStatus", &MemoryManager::DumpStatus)
	;
	
	class_< ScriptFunctorWrapper, GcReference<ScriptFunctorWrapper>, boost::noncopyable > ("Functor")
			.def("Invoke", pure_virtual(&Functor::operator ()))
	;
	implicitly_convertible<GcReference<ScriptFunctorWrapper>,GcReference<Functor> >();
	
	class_<ScriptFunctionWrapper, GcReference<ScriptFunctionWrapper> > ("Function",
		init<object>())
	;
	implicitly_convertible<GcReference<ScriptFunctionWrapper>,GcReference<Functor> >();

	class_<ScriptEventHandler> ("EventHandler",
		init<object>())
		.def(init<object, EventHandler::tPriority>())
	;
	implicitly_convertible<ScriptEventHandler, EventHandler>();
	
	class_<Event, GcReference<Event> >("Event")
	;

	class_<GameState, GameState::tRef>("GameState")
		.def("BindEvent", &GameState::BindEvent,
			with_custodian_and_ward<1,2>())
		.def("ClearEvents", &GameState::ClearEvents)
		.def("RemoveHandler", &GameState::RemoveHandler)
		.add_property("timeScale", &GameState::getTimeScale, &GameState::setTimeScale)
	;

	class_<GameStateWrapper, GameStateWrapper::tRef, boost::noncopyable>("CustomGameState")
		.def("BindEvent", &GameState::BindEvent,
			with_custodian_and_ward<1,2>())
		.def("ClearEvents", (void (GameState::*)(const Event::tRef&))&GameState::ClearEvents)
		.def("RemoveHandler", &GameState::RemoveHandler)
		.add_property("timeScale", &GameState::getTimeScale, &GameState::setTimeScale)
	;
	implicitly_convertible<GameStateWrapper::tRef, GameState::tRef>();


	class_<Kernel>("Kernel")
		.def("GetInst", &Kernel::GetInstancePtr,
			return_value_policy<reference_existing_object>())
		.staticmethod("GetInst")
		.def("GetTime", &Kernel::GetTime)

		.def("ProcessEvent", &Kernel::ProcessEvent)
		.def("BindEvent", &Kernel::BindEvent)
		.def("ClearEvents", &Kernel::ClearEvents)
		.def("RemoveHandler", &Kernel::RemoveHandler)
		.add_property("eventStatus", &Kernel::GetEventStatus, &Kernel::SetEventStatus)

		.def("GetGameState", &Kernel::GetGameStatePtr,
			return_value_policy<reference_existing_object>())
		.def("ForkGameState", &Kernel::ForkGameState)
		.def("PushGameState", &Kernel::PushGameState)
		.def("PopGameState", (void (Kernel::*)(void))&Kernel::PopGameState)
	;

	class_<IObservable, IObservable::tRef, boost::noncopyable>("IObservable", no_init)
		.def("AddObserver", &IObservable::AddObserver)
		.def("RemoveObserver", &IObservable::RemoveObserver)
		.def("NotifyObservers", &IObservable::NotifyObservers)
	;



	/**** Maths ****/
	
	class_<tVector>("Vector", init<tScalar, tScalar, tScalar>())
		.def(init<tVector>())
		.def(init<>())
		.add_property("x", make_function(&tVector::get<0>, return_value_policy<copy_const_reference>()), &tVector::set<0>)
		.add_property("y", make_function(&tVector::get<1>, return_value_policy<copy_const_reference>()), &tVector::set<1>)
		.add_property("z", make_function(&tVector::get<2>, return_value_policy<copy_const_reference>()), &tVector::set<2>)
	;
	class_<iVector2D>("iVector2D", init<int, int>())
		.def(init<iVector2D>())
		.def(init<>())
		.add_property("x", make_function(&iVector2D::get<0>, return_value_policy<copy_const_reference>()), &iVector2D::set<0>)
		.add_property("y", make_function(&iVector2D::get<1>, return_value_policy<copy_const_reference>()), &iVector2D::set<1>)
	;
	class_<fVector4D>("ColourVal", init<tScalar, tScalar, tScalar, tScalar>())
		.def(init<fVector4D>())
		.def(init<>())
		.add_property("r", make_function(&fVector4D::get<0>, return_value_policy<copy_const_reference>()), &fVector4D::set<0>)
		.add_property("g", make_function(&fVector4D::get<1>, return_value_policy<copy_const_reference>()), &fVector4D::set<1>)
		.add_property("b", make_function(&fVector4D::get<2>, return_value_policy<copy_const_reference>()), &fVector4D::set<2>)
		.add_property("a", make_function(&fVector4D::get<3>, return_value_policy<copy_const_reference>()), &fVector4D::set<3>)
	;
	class_<Angle>("Angle", init<tScalar>())
	;
	class_<Orientation3D>("Orientation", init<>())
		.def("Rotation", &Orientation3D::Rotation)
		.staticmethod("Rotation")
		.def("After", (Orientation3D (Orientation3D::*)(const Orientation3D&) const) &Orientation3D::operator *)
	;


	/**** Input ****/
	
	class_<InputHandler>("InputHandler")
		.def("GetInst", &InputHandler::GetInstancePtr,
			return_value_policy<reference_existing_object>())
		.staticmethod("GetInst")
		.def("CaptureAllInput", &InputHandler::CaptureAllInput)
		.def("EnterGameMode", &InputHandler::EnterGameMode)
		.def("EnterGuiMode", &InputHandler::EnterGuiMode)
		.add_property("mousePosition", &InputHandler::GetMousePosition)
	;


	class_<MouseMoveEvent, bases<Event>, GcReference<MouseMoveEvent> >("MouseMoveEvent")
		.add_property("dx", &MouseMoveEvent::GetDeltaX)
		.add_property("dy", &MouseMoveEvent::GetDeltaY)
		.add_property("x", &MouseMoveEvent::x)
		.add_property("y", &MouseMoveEvent::y)
	;
	implicitly_convertible<GcReference<MouseMoveEvent>,GcReference<Event> >();
	
	class_<MouseDownEvent, bases<Event>, GcReference<MouseDownEvent> >("MouseDownEvent")
		.add_property("x", &MouseDownEvent::x)
		.add_property("y", &MouseDownEvent::y)
	;
	implicitly_convertible<GcReference<MouseMoveEvent>,GcReference<Event> >();

	class_<MouseUpEvent, bases<Event>, GcReference<MouseUpEvent> >("MouseUpEvent")
		.add_property("x", &MouseUpEvent::x)
		.add_property("y", &MouseUpEvent::y)
	;
	implicitly_convertible<GcReference<MouseMoveEvent>,GcReference<Event> >();


	class_<InputKey>("InputKey", init<>())
#	ifdef __INPUT_SDL__
		.def(init<SDL_keysym>())
#	endif
		.def(init<int>())
		.def(init<int, int>())
	;
	
	class_<KeyEvent, bases<Event>, GcReference<KeyEvent> >("KeyEvent", init<>())
		.def(init<InputKey>())
		.def("getKey", &KeyEvent::getKey,
			return_value_policy<copy_const_reference>())
	;
	implicitly_convertible<GcReference<KeyEvent>,GcReference<Event> >();
	
	class_<KeyDownEvent, bases<Event>, GcReference<KeyDownEvent> >("KeyDownEvent", init<InputKey>())
	;
	implicitly_convertible<GcReference<KeyDownEvent>,GcReference<Event> >();
	
	class_<KeyUpEvent, bases<Event>, GcReference<KeyUpEvent> >("KeyUpEvent", init<InputKey>())
	;
	implicitly_convertible<GcReference<KeyUpEvent>,GcReference<Event> >();
	

	/**** Game Subsystem ****/
	
	// Basic camera
	class_<Camera, Camera::tRef>("Camera", init<tVector, tVector, tVector>())
		.add_property("position", &Camera::getPosition, &Camera::setPosition)
		.add_property("direction", (tVector (Camera::*)(void))&Camera::getDirection, (void (Camera::*)(const tVector&))&Camera::setDirection)
		.add_property("upDirection", &Camera::getUpDirection, &Camera::setUpDirection)
	;
	implicitly_convertible<Camera::tRef, ICamera::tRef>();
	implicitly_convertible<ICamera::tRef, IPerspective::tRef>();

	class_<GameManager>("GameManager", no_init)
		.def("GetInst", &GameManager::GetInstancePtr,
				 return_value_policy<reference_existing_object>())
		.staticmethod("GetInst")
		.def("SaveGameToFile", &GameManager::SaveGameToFile)
		.def("LoadGameFromFile", &GameManager::LoadGameFromFile)
	;

	class_<IGameObject, IGameObject::tRef, boost::noncopyable>("IGameObject", no_init)
		.def("Destroy", &IGameObject::Destroy)
		.add_property("name", &IGameObject::getName)
		.add_property("owned", &IGameObject::isOwned)
	;

	class_<GameObject::tCreationOptions>("GameObjectCreationOptions")
		.def_readwrite("name", &GameObject::tCreationOptions::sName)
		.def_readwrite("priority", &GameObject::tCreationOptions::Priority)
		// IsOwner property not changable from script (all objects created from script are owned)
	;
	class_<GameObject, GameObject::tRef, bases<IGameObject> >("GameObject", no_init)
	;
	implicitly_convertible<GcReference<GameObject>, GcReference<IGameObject> >();

	class_<Renderer>("Renderer", no_init)
		.def("GetInst", &Renderer::GetInstancePtr,
				 return_value_policy<reference_existing_object>())
		.staticmethod("GetInst")
		.def("Render", &Renderer::Render)
		.def("PushObject", &Renderer::PushObject)
		.def("RemoveObject", &Renderer::RemoveObject)
		.add_property("camera", make_function(&Renderer::GetCamera, return_value_policy<reference_existing_object>()), &Renderer::SetCamera)
	;

	class_<PhysicsEngine::tObjectList>("WorldObjectList")
		.def(list_indexing_suite<PhysicsEngine::tObjectList>())
	;
	class_<PhysicsEngine::tConstraintList>("ConstraintList")
		.def(list_indexing_suite<PhysicsEngine::tConstraintList>())
	;
	class_<PhysicsEngine>("PhysicsEngine", no_init)
		.add_property("objects", make_function(&PhysicsEngine::getObjects, return_value_policy<reference_existing_object>()))
		.add_property("constraints", make_function(&PhysicsEngine::getConstraints, return_value_policy<reference_existing_object>()))
			.def("GetInst", &PhysicsEngine::GetInstancePtr,
				 return_value_policy<reference_existing_object>())
			.staticmethod("GetInst")
	;
	
	class_<WorldObject, GcReference<WorldObject>, bases<GameObject> >("WorldObject", no_init)
		.add_property("position", &WorldObject::getPosition, &WorldObject::setPosition)
		.add_property("rotation", &WorldObject::getRotation, &WorldObject::setRotation)
		.add_property("orientation", &WorldObject::getOrientation, &WorldObject::setOrientation)
		.def("Destroy", &WorldObject::Destroy)
	;
	//implicitly_convertible<GcReference<WorldObject>, GcReference<IRenderable> >();
	implicitly_convertible<GcReference<WorldObject>, GcReference<GameObject> >();
	
	class_<Physical::Force, GcReference<Physical::Force>, bases<tVector> >("Force", init<tVector, tVector>())
			.def(init<tVector>())
			.def(init<>())
	;
	
	class_<Physical::tCreationOptions, bases<WorldObject::tCreationOptions> >("PhysicalCreationOptions")
		
	;
	class_<Physical, GcReference<Physical>, bases<WorldObject> >("Physical",
			init<const Physical::tCreationOptions&, GraphicsData::tCacheRef, PhysicalModel::tCacheRef>())
		.add_property("velocity", &Physical::getVelocity, &Physical::setVelocity)
		.def("AttachForce", (void (Physical::*)(const Physical::Force::tRef &))&Physical::applyForce)
		.def("ApplyForce", (void (Physical::*)(tVector, tVector))&Physical::applyForce)
		.def("ApplyImpulse", &Physical::applyImpulse)
		.def("Destroy", &Physical::Destroy)
		.def("Activate", &Physical::setActive)
	;
	
	class_<Static::tCreationOptions, bases<WorldObject::tCreationOptions> >("StaticCreationOptions")
		
	;
	class_<Static, GcReference<Static>, bases<WorldObject> >("Static",
			init<const Static::tCreationOptions&, GraphicsData::tCacheRef, PhysicalModel::tCacheRef>())
		.def("Destroy", &Static::Destroy)
	;
	
	implicitly_convertible<GcReference<Physical>,GcReference<WorldObject> >();
	implicitly_convertible<GcReference<Static>,GcReference<WorldObject> >();
	
	class_<PointConstraint, GcReference<PointConstraint>, bases<> >("PointConstraint",
			init<GcReference<WorldObject>, GcReference<WorldObject>,
   				tVector, tVector>())
	;
	/*class_<SliderConstraint, GcReference<SliderConstraint>, bases<> >("SliderConstraint",
			init<GcReference<WorldObject>, GcReference<WorldObject>,
   		tVector, tVector, tVector, tVector>())
	;
	class_<SliderConstraint, GcReference<SliderConstraint>, bases<> >("SliderConstraint",
			init<GcReference<WorldObject>, GcReference<WorldObject>,
   		tVector, tVector, tVector, tVector>())
	;*/
	class_<SliderConstraint, GcReference<SliderConstraint>, bases<> >("SliderConstraint",
			init<GcReference<WorldObject>, GcReference<WorldObject>,
   				tVector, tVector, tVector, tVector>())
	;
	
	class_<ObjectEvent, GcReference<ObjectEvent> >("ObjectEvent", init<const GameObject::tRef&>())
		.add_property("object", make_function(&ObjectEvent::getObject, return_value_policy<reference_existing_object>()));
	implicitly_convertible<GcReference<ObjectEvent>,GcReference<Event> >();

	class_<ObjectInteractionEvent, GcReference<ObjectInteractionEvent>, bases<ObjectEvent> >
		("ObjectInteractionEvent", init<const GameObject::tRef&, const GameObject::tRef&>())
		.add_property("interactingObject", make_function(&ObjectInteractionEvent::getInteractingObject, return_value_policy<reference_existing_object>()));
	implicitly_convertible<GcReference<ObjectInteractionEvent>,GcReference<ObjectEvent> >();
	


	/**** More Cameras ****/

	class_<MovableCamera, MovableCamera::tRef, bases<Camera> >("MovableCamera", init<const tVector&, const tVector&, const tVector&>())
		.def("BindToKeyboardAndMouse", &MovableCamera::BindToKeyboardAndMouse)
	;
	implicitly_convertible<MovableCamera::tRef, Camera::tRef>();

	class_<PannableCamera::tCreationOptions>("PannableCameraOptions")
		.def_readwrite("maxDistance", &PannableCamera::tCreationOptions::fMaxDistance)
		.def_readwrite("minDistance", &PannableCamera::tCreationOptions::fMaxDistance)
		.def_readwrite("zoomSpeed", &PannableCamera::tCreationOptions::fMaxDistance)
		.def_readwrite("scrollSpeed", &PannableCamera::tCreationOptions::fMaxDistance)
		.def_readwrite("edgeScrolling", &PannableCamera::tCreationOptions::fMaxDistance)
	;
	class_<PannableCamera, PannableCamera::tRef, bases<Camera> >("PannableCamera", init<const PannableCamera::tCreationOptions&, const tVector&, const tVector&>())
		.def("BindToKeyboardAndMouse", &PannableCamera::BindToKeyboardAndMouse)
	;
	implicitly_convertible<PannableCamera::tRef, Camera::tRef>();



	/**** Data Subsystem ****/

	class_<DataManager::tObjectMap>("ObjectMap")
		.def(map_indexing_suite<DataManager::tObjectMap>())
	;
	class_<DataManager>("DataManager")
		.def("GetInst", &DataManager::GetInstancePtr,
			 return_value_policy<reference_existing_object>())
		.staticmethod("GetInst")
		.def("Insert", &DataManager::Insert)
		.add_property("objects", make_function(&DataManager::getObjectMap, return_value_policy<reference_existing_object>()))
	;

	/*class_<WeakRef<WtfEngine::IDataObject> >("DataObjectWeakRef", no_init)
		.add_property("ref", make_function(&WeakRef<WtfEngine::IDataObject>::operator *, return_value_policy<reference_existing_object>()))
	;*/
	//class_<WtfEngine::IDataObject::tCacheRef, bases<WeakRef<WtfEngine::IDataObject> > >("DataObjectRef", no_init)
	//;
	class_<WtfEngine::IDataObject, WtfEngine::IDataObject::tCacheRef, boost::noncopyable>("DataObject", no_init)
		.add_property("name", &WtfEngine::IDataObject::getName)
	;


	// Physical models

	class_<PhysicalModel, PhysicalModel::tRef, bases<WtfEngine::IDataObject>, boost::noncopyable >("PhysicalModel", no_init);
	DECL_CACHE_REF("ModelRef", PhysicalModel, WtfEngine::IDataObject);

	class_<PhysicalModels::Cube, GcReference<PhysicalModels::Cube>, bases<PhysicalModel> >
			("CubeModel", init<const String&, tScalar>());
	class_<PhysicalModels::Oblong, GcReference<PhysicalModels::Oblong>, bases<PhysicalModel> >
			("OblongModel", init<const String&, const tVector&>());
	class_<PhysicalModels::Sphere, GcReference<PhysicalModels::Sphere>, bases<PhysicalModel> >
			("SphereModel", init<const String&, tScalar, unsigned>());
	class_<PhysicalModels::Cylinder, GcReference<PhysicalModels::Cylinder>, bases<PhysicalModel> >
			("CylinderModel", init<const String&, tScalar, tScalar, unsigned>());
	class_<PhysicalModels::Point, GcReference<PhysicalModels::Point>, bases<PhysicalModel> >
			("PointModel", init<const String&>());
	class_<PhysicalModels::Compound, GcReference<PhysicalModels::Compound>, bases<PhysicalModel> >
			("CompoundModel", init<const String&>())
		.def(init<const String&, tScalar>())
		.def("InsertModel",	&PhysicalModels::Compound::InsertModel)
	;

	
	implicitly_convertible<GcReference<PhysicalModels::Cube>,PhysicalModel::tRef >();
	implicitly_convertible<GcReference<PhysicalModels::Oblong>,PhysicalModel::tRef >();
	implicitly_convertible<GcReference<PhysicalModels::Sphere>,PhysicalModel::tRef >();
	implicitly_convertible<GcReference<PhysicalModels::Cylinder>,PhysicalModel::tRef >();
	implicitly_convertible<GcReference<PhysicalModels::Point>,PhysicalModel::tRef >();
	implicitly_convertible<GcReference<PhysicalModels::Compound>,PhysicalModel::tRef >();


	// Graphics Data

	class_<GraphicsData, GraphicsData::tCacheRef, bases<WtfEngine::IDataObject>, boost::noncopyable >("GraphicsData", no_init);
	implicitly_convertible<GraphicsData::tCacheRef, GraphicsData::tRef>();
	implicitly_convertible<GraphicsData::tCacheRef, WtfEngine::IDataObject::tCacheRef>();

	class_<TransformedMesh, TransformedMesh::tRef>("TransformedMesh", init<GraphicsData::tCacheRef, tVector, Orientation3D>());
	implicitly_convertible<TransformedMesh::tRef, GraphicsData::tCacheRef>();

	class_<Mesh, Mesh::tRef, bases<GraphicsData> >("Mesh", init<tConstString>())
		.def("InsertMesh", &Mesh::addMesh)
	;
	DECL_CACHE_REF("MeshRef", Mesh, GraphicsData);

	class_<Model, Model::tRef, bases<GraphicsData> >("Model", init<tConstString>())
		.def("InsertMesh", &Model::InsertMesh)
	;
	DECL_CACHE_REF("ModelRef", Model, GraphicsData);


	class_<Material::tCreationOptions>("MaterialCreationOptions")
		.def_readwrite("diffuse", &Material::tCreationOptions::vDiffuse)
		.def_readwrite("specular", &Material::tCreationOptions::vSpecular)
		.def_readwrite("emissive", &Material::tCreationOptions::vEmissive)
		.def_readwrite("power", &Material::tCreationOptions::fPower)
	;
	class_<Material, Material::tRef, bases<WtfEngine::IDataObject>, boost::noncopyable >("Material",
		init<tConstString, const Material::tCreationOptions&>())
	;
	DECL_CACHE_REF("MaterialRef", Material, WtfEngine::IDataObject);

	class_<MaterialBinding, MaterialBinding::tRef, bases<GraphicsData> >("MaterialBinding",
		init<tConstString, Material::tCacheRef, GraphicsData::tCacheRef>())
		.add_property("material", make_function(&MaterialBinding::getMaterial, return_value_policy<copy_const_reference>()))
		.add_property("graphics", make_function(&MaterialBinding::getGraphics, return_value_policy<copy_const_reference>()))
	;
	implicitly_convertible<MaterialBinding::tRef, GraphicsData::tCacheRef>();


	class_<XMesh, XMesh::tRef, bases<GraphicsData>, boost::noncopyable >("XMesh", no_init);
	DECL_CACHE_REF("XMesh", XMesh, GraphicsData);
	class_<XModel, XModel::tRef, bases<GraphicsData>, boost::noncopyable >("XModel", no_init);
	DECL_CACHE_REF("XModel", XModel, GraphicsData);

	
	class_<Texture, Texture::tRef, bases<WtfEngine::IDataObject> >("Texture", no_init)
		.add_property("w", &Texture::getWidth)
		.add_property("h", &Texture::getHeight)
		.add_property("size", make_function(&Texture::getSize, return_value_policy<copy_const_reference>()))
		.def("Read", (void (Texture::*)(const String&, bool))&Texture::Read)
		.def("Write", (void (Texture::*)(const String&))&Texture::Write)
	;
	DECL_CACHE_REF("TextureRef", Texture, WtfEngine::IDataObject);

	class_<FBOTexture, FBOTexture::tRef, bases<Texture> >("FBOTexture")

	;
	

	/**** Primitives ****/
	
	class_<PrimitiveModel, PrimitiveModel::tRef, bases<GraphicsData> >("PrimitiveModel", init<tConstString>())
		.def(init<tConstString, const PrimitiveModel::IPrimitive::tRef&>())
		.def("InsertPrimitive", &PrimitiveModel::InsertPrimitive)
	;
	implicitly_convertible<PrimitiveModel::tRef,GraphicsData::tCacheRef>();

	class_<Primitives::Cube, GcReference<Primitives::Cube>, bases<PrimitiveModel::IPrimitive> >
			("Cube", init<tScalar>());
	class_<Primitives::Oblong, GcReference<Primitives::Oblong>, bases<PrimitiveModel::IPrimitive> >
			("Oblong", init<tScalar, tScalar, tScalar>());
	class_<Primitives::Sphere, GcReference<Primitives::Sphere>, bases<PrimitiveModel::IPrimitive> >
			("Sphere", init<tScalar, int, int>());
	class_<Primitives::Cylinder, GcReference<Primitives::Cylinder>, bases<PrimitiveModel::IPrimitive> >
			("Cylinder", init<tScalar, tScalar, int>());
	
	implicitly_convertible<GcReference<Primitives::Cube>,PrimitiveModel::IPrimitive::tRef>();
	implicitly_convertible<GcReference<Primitives::Oblong>,PrimitiveModel::IPrimitive::tRef>();
	implicitly_convertible<GcReference<Primitives::Sphere>,PrimitiveModel::IPrimitive::tRef>();
	implicitly_convertible<GcReference<Primitives::Cylinder>,PrimitiveModel::IPrimitive::tRef>();
	

	/**** Tools ****/

	class_<ObjectSelection<WorldObject>::tObjectList >("ObjectSelectionList")
		.def(set_indexing_suite<ObjectSelection<WorldObject>::tObjectList>())
	;
	class_<ObjectSelection<WorldObject>, ObjectSelection<WorldObject>::tRef >("ObjectSelection", init<>())
		.def("Select", &ObjectSelection<WorldObject>::Select)
		.def("Clear", &ObjectSelection<WorldObject>::Clear)
		.def("Init", &ObjectSelection<WorldObject>::Init)
		.def("Destroy", &ObjectSelection<WorldObject>::Destroy)
		.add_property("selection", make_function(&ObjectSelection<WorldObject>::getSelection, return_value_policy<reference_existing_object>()))
		.add_property("onChange", make_function(&ObjectSelection<WorldObject>::getOnChange, return_value_policy<copy_const_reference>()), &ObjectSelection<WorldObject>::setOnChange)
	;

	class_<ObjectTool, ObjectTool::tRef, boost::noncopyable>("ObjectTool", no_init)
		.def("Init", &ObjectTool::Init)
		.def("Destroy", &ObjectTool::Destroy)
	;

	class_<ObjectMoveTool, ObjectMoveTool::tRef, bases<ObjectTool> >("ObjectMoveTool",
		init<const ObjectSelectionTool::tRef&>());
	implicitly_convertible<ObjectMoveTool::tRef, ObjectTool::tRef>();
	
	class_<ObjectScaleTool, ObjectScaleTool::tRef, bases<ObjectTool> >("ObjectScaleTool",
		init<const ObjectSelectionTool::tRef&>());
	implicitly_convertible<ObjectScaleTool::tRef, ObjectTool::tRef>();


	/**** Misc ****/

#ifdef __UNICODE__
	class_<String>("String", init<tConstString>());

	implicitly_convertible<std::string,String>();
	implicitly_convertible<String,std::string>();
#endif

	implicitly_convertible<String, StringPool::Entry>();
	to_python_converter<StringPool::Entry, StringPoolEntryConverter, true>();

	def("DebugPrint", &ScriptHost::_DebugPrint);
	def("ConsolePrint", &ScriptHost::_ConsolePrint);
};


extern "C" void initWtfNet();

ScriptHost::ScriptHost()
: Singleton<ScriptHost>(), Loggable<ScriptHost>("Script Host"){
#ifdef SCRIPTING_LUA
	mpState = luaL_newstate();
	luaL_openlibs(mpState);

	if(mpState == NULL)
		throw "Failed to initialize Lua";

	luabind::open(mpState);

	luabind::module(mpState)[
		luabind::def("DebugPrint", &_DebugPrint)
	];
#else
	
	// Initialise python
	Py_SetProgramName("wtfexe");
	Py_InitializeEx(0);
	EvalScript("import sys; sys.path.append(\".\");");
	
	initWtfEngine();
	initWtfNet();

#endif
};

ScriptHost::~ScriptHost(){
#ifdef SCRIPTING_LUA
	lua_close(mpState);
#else
	Py_Finalize();
#endif
};

void ScriptHost::LoadScript(tString sFile)
{
#ifdef SCRIPTING_LUA
	try{
		if(luaL_dofile(mpState, sFile) != 0)
			throw "Lua: failed to load script";
	}catch(const std::exception& e){
		std::cerr << "**ERROR Lua #" << lua_status(mpState)
			<< ": " << e.what() << std::endl;
	};
#else
	try{
		object mainMod = import("__main__");
		object mainNs = mainMod.attr("__dict__");

		boost::python::exec_file(sFile, mainNs, mainNs);
	} catch(const error_already_set &) {
		PyErr_Print();
		throw std::logic_error("Evaluation failed");
	};
#endif
};

void ScriptHost::EvalScript(const String& sScript)
{
#ifdef SCRIPTING_LUA
	if(luaL_dostring(mpState, sScript) != 0)
		throw std::runtime_error("Evaluation failed");
#else
	try{
		PyGILState_STATE gstate;
		gstate = PyGILState_Ensure();

		object mainMod = import("__main__");
		object mainNs = mainMod.attr("__dict__");

		//if(sScript.IndexOf(';') != -1)
			boost::python::exec(sScript.c_str(), mainNs, mainNs);
		//else
		//	_ConsolePrint(String(extract<std::string>(boost::python::eval(((std::string)sScript).c_str(), mainNs, mainNs))));
		PyGILState_Release(gstate);
	} catch(const error_already_set &) {
		PyErr_Print();
		throw std::runtime_error("Evaluation failed");
	};
#endif
};

/** API **/

void ScriptHost::_DebugPrint(const String& s){
	GetInstance().Log(LOG_DEBUG, s);
};
void ScriptHost::_ConsolePrint(const String& s){
	if(WtfGui::Console::GetActiveConsole().isNull())
		GetInstance().Log(LOG_INFO, s);
	else
		WtfGui::Console::GetActiveConsole()->Print(s);
};

